package main;

import java.util.HashMap;
import java.util.Vector;

public class ScienceStoreImpl implements ScienceStore {
	
	private HashMap<String, Vector<Pack<Integer>>> equipmentPacks;
	private HashMap<String, Vector<Pack<Lab>>> LabPacks;
	private HashMap<String, Vector<Pack<Scientist>>> ScientistPacks;
	
	public ScienceStoreImpl()
	{
		this.equipmentPacks = new HashMap<String, Vector<Pack<Integer>>>(30);
		this.LabPacks =new HashMap<String, Vector<Pack<Lab>>>();
		this.ScientistPacks=new HashMap<String, Vector<Pack<Scientist>>>();
	}
	
	public void addEquipmentPack(String name, int price, int amount)
	{
		if(!this.equipmentPacks.containsKey(name))
		{
			this.equipmentPacks.put(name,new Vector<Pack<Integer>>());
		}
		int index=getPlaceAtShelf(price,equipmentPacks.get(name));
		//no need to create equipment since its primitive int
		//create new Pack
		Pack<Integer> newPack=new Pack<Integer>(amount,price,index);
		equipmentPacks.get(name).add(index, newPack);
	}
	
	public void addLabPack(String headLabName, String spec, int price, int numOfScientists)
	{
		if(!this.LabPacks.containsKey(spec))
		{
			this.LabPacks.put(spec,new Vector<Pack<Lab>>());
		}
		int index=getPlaceAtShelf(price,LabPacks.get(spec));
		//create Lab
		Lab lab=new Lab(spec,numOfScientists,headLabName);
		//create new Pack
		Pack<Lab> newPack=new Pack<Lab>(lab,price,index);
		LabPacks.get(spec).add(index, newPack);
	}
	
	public void addScientistPack(String spec, int price, String scientistName)
	{
		if(!this.ScientistPacks.containsKey(spec))
		{
			this.ScientistPacks.put(spec,new Vector<Pack<Scientist>>());
		}
		int index=getPlaceAtShelf(price,ScientistPacks.get(spec));
		//create Scientist
		Scientist scientist=new Scientist(spec, scientistName);
		//create new Pack
		Pack<Scientist> newPack=new Pack<Scientist>(scientist,price,index);
		ScientistPacks.get(spec).add(index, newPack);
	}
	
private <T> int getPlaceAtShelf(int price,Vector<Pack<T>> packs) {
	int index=0;	
	while((packs.size()-1)>index && packs.get(index).getPrice()<price)
	{
		index++;
	}
	return index;
	}

public Integer buyEquipment(String name, int amount, Statistics stats) {
	Pack<Integer> pack=browseEquipment(name, amount);
	if(pack==null)
	{
		return 0;
	}
	else
	{
		sellEquipment(name, pack.getIndex());
		charge(pack.getPrice(),stats);
		stats.addEquipmentPack(name, pack);
		return new Integer(pack.getData().intValue());
	}
}

	
	public Lab buyLab(String spec, Statistics stats) {
		Pack<Lab> pack=browseLab(spec);
		if(pack==null)
		{
			return null;
		}
		else
		{
			sellLab(spec);
			charge(pack.getPrice(),stats);
			stats.addLabPack(spec, pack);
			return pack.getData();
		}
	}

	
	public Scientist buyScientist(String spec, Statistics stats) {
		Pack<Scientist> pack=browseScientist(spec);
		if(pack==null)
		{
			return null;
		}
		else
		{
			sellScientist(spec);
			charge(pack.getPrice(),stats);
			stats.addScientistPack(spec, pack);
			return pack.getData();
		}
	}
	
	/**
	 * a private function used by buyEquipment and searches for the cheapest pack matching the requirements
	 * @param name - the name of the equipment to buy
	 * @param amount - the minimal amount required to be in the package
	 * @return a pack object containing the amount in the pack, the price and the index in the store container to be deleted
	 */

	 protected Pack<Integer> browseEquipment(String name,int amount)
	 { 
		 Pack<Integer> unionPack=null;
		 if(equipmentPacks.containsKey(name))
		 {
			 // buy cheapest single pack than contain all amount needed
			 for(int i=0;i<getEquipmentLength(name) && unionPack==null;i++)
			 {
				 if(getEquipmentAtIndex(name, i).getData().intValue()>=amount)
				 {
					 unionPack=getEquipmentAtIndex(name, -(i));   // minus to represent a singe pack
				 }
			 }
			 if(unionPack==null) // if there wasnt a single pack with amount needed
			 {
				 // try buying all amount in several packs, cheapest ones you can find
				 int i=0;
				 int unionAmount=0;
				 int unionPrice=0;
				 int unionIndex=0;
				 while(i<getEquipmentLength(name) && (amount-unionAmount)>0)
				 {
					 unionAmount=unionAmount + getEquipmentAtIndex(name, i).getData().intValue();
					 unionPrice=unionPrice + getEquipmentAtIndex(name, i).getPrice();
					 unionIndex=getEquipmentAtIndex(name, i).getIndex();
					 i++;
				 } 
		
				 //create new pack
				 if(unionAmount!=0)		//if there is a pack at all available
					 unionPack=new Pack<Integer>(unionAmount, unionPrice, unionIndex);
			 }  
		 }	 
		 
		 return unionPack;
	 }
	/**
	 * a private function used by buyLab and searches for the cheapest pack matching the requirements
	 * 
	 * @param spec - a string representing the required specialty for the lab
	 * @return a pack object containing the Lab object itself and the price to be charged.
	 */
	protected Pack<Lab> browseLab(String spec)
	{
		if(LabPacks.containsKey(spec))
			return getLabAtIndex(spec, 0);
		else 
			return null;
	}
	/**
	 * a private function used by buyScientist and searches for the cheapest pack matching the requirements
	 * @param spec - a string representing the required specialty for the scientist
	 * @return a pack object containing the Scientist object itself and the price to be charged.
	 */
	protected Pack<Scientist> browseScientist(String name)
	{
		if(ScientistPacks.containsKey(name))
			return getScientistAtIndex(name, 0);
		else 
			return null;
	}
	/**
	 * the sell function called by buyEquipment and deletes the sold equipment from the container
	 * @param i - if i<=0 deletes the pack in the i index, if i>0 deletes the packs on indexes starting from 0 up to i
	 */
	protected void sellEquipment(String name,int i)
	{
		Vector < Pack<Integer>> e=equipmentPacks.get(name);
		if(i>0)
		{
			while(i>=0)
			{	
				e.removeElementAt(0);
				i--;
			}
		}
		else if(i<=0)
			e.removeElementAt(i);
	}
	/**
	 * the sell function called by buyLab and deletes the sold Lab from the container
	 */
	void sellLab(String spec)
	{
		LabPacks.get(spec).removeElementAt(0);
	}
	/**
	 * the sell function called by buyScientist and deletes the sold scientist from the container
	 */
	protected void sellScientist(String spec)
	{
		ScientistPacks.get(spec).removeElementAt(0);
	}
	/**
	 * a function that charges the company budget for the specified amount
	 * @param price - the amount to remove from the budget
	 * @param stats - a pointer to the statistics object for charging the budget 
	 */
	protected void charge(int price,Statistics stats)
	{
		stats.useBudget(price);
	}


	public int getScientistLength(String spec) {
		return ScientistPacks.get(spec).size();
	}


	public Pack<Scientist> getScientistAtIndex(String spec, int index) {
		if(getScientistLength(spec)>index)
			return ScientistPacks.get(spec).elementAt(index);
		else
		{
			System.out.println("for scientist "+spec+" not exist pack at index "+index);
			return null;
		}
			
	}


	public int getLabLength(String spec) {
			return LabPacks.get(spec).size();
	}


	public Pack<Lab> getLabAtIndex(String spec, int index) {
		if(getLabLength(spec)>index)
			return LabPacks.get(spec).elementAt(index);
		else
		{
			System.out.println("for lab "+spec+" not exist pack at index "+index);
			return null;
		}	
	}


	public int getEquipmentLength(String name) {
		return equipmentPacks.get(name).size();
	}


	public Pack<Integer> getEquipmentAtIndex(String name, int index) {
		if(getEquipmentLength(name)>index)
			return equipmentPacks.get(name).elementAt(index);
		else
		{	
			System.out.println("for equipment "+name+" not exist pack at index "+index);
			return new Pack<Integer>(0,0);
		}	
	}


/*	
	public ScienceStoreImpl(ScienceStoreImpl store) {
		this.equipmentPacks=new HashMap<>(store.equipmentPacks);
		this.LabPacks=new HashMap<>(store.LabPacks);
		this.ScientistPacks=new HashMap<>(store.ScientistPacks);
	}
*/
/*
/**
 * 
 * @param equipmentPacks - should be sorted, otherwise exception thrown
 * @param LabPacks - should be sorted, otherwise exception thrown
 * @param ScientistPacks - should be sorted, otherwise exception thrown
 */
	
	/*
	public ScienceStoreImpl(HashMap<String, Vector<Pack<Integer>>> equipmentPacks, HashMap<String, Vector<Pack<Lab>>> LabPacks, HashMap<String, Vector<Pack<Scientist>>> ScientistPacks) throws RuntimeException{	
		if(!isPacksSorted(equipmentPacks))
			throw new RuntimeException("equipmentPacks is not sorted!"); 
		if(!isPacksSorted(LabPacks))
			throw new RuntimeException("LabPacks is not sorted!");
		if(!isPacksSorted(ScientistPacks))
			throw new RuntimeException("ScientistPacks is not sorted!");
		
		copyPacks(equipmentPacks);
		this.LabPacks=new HashMap<>(LabPacks);
		this.ScientistPacks=new HashMap<>(ScientistPacks);
	}


	private void copyEquipmentPacks(HashMap<String, Vector<Pack<Integer>>> packs) {
	this.equipmentPacks=new HashMap<>(equipmentPacks);	
	Iterator<Vector <Pack<Integer> >> i=packs.values().iterator();
	while(i.hasNext())
	{
		Vector <Pack<Integer> > oldPacks=i.next();
		Vector <Pack<Integer> > copiedPacks=new Vector<Pack<Integer>>();
		for(int j=0;j<=oldPacks.size();j++)	
		{
			getEquipmentAtIndex(i., index)
			Pack<Integer> newPack=
					
		}
			copiedPacks.addElement(oldPacks.elementAt(j).);
	}
			
	
}

	private <T> boolean isPacksSorted(HashMap<String, Vector<Pack<T>>> packs) {
		boolean ans=true;
		Iterator<Vector <Pack<T> >> i=packs.values().iterator();
		while(i.hasNext() && ans)
		{
			Vector <Pack<T> > sortedPacks=i.next();
			for(int j=0;j<=sortedPacks.size()-1 && ans;j++)
				if(!sortedPacks.elementAt(j).isCheaperPack(sortedPacks.elementAt(j+1)))
					ans=false;
		}
		return ans;
	}

*/


}
